Skip to main content

Arm Performance Libraries替代Eigen的后端

在将 Eigen 的后端替换为 Arm Performance Libraries(APL,Arm 性能库,包含 Arm BLAS、LAPACK 等线性代数优化实现)时,核心思路是让 Eigen 调用 APL 提供的经过 Arm 架构优化的底层线性代数函数(如矩阵乘法、分解等),而非 Eigen 自带的通用实现,从而利用 APL 针对 Arm CPU(如 A55 等)的指令集(如 NEON)和架构特性优化,提升计算性能。以下是具体步骤和注意事项:

一、原理:Eigen 对外部 BLAS/LAPACK 的支持

Eigen 本身是一个纯头文件库,其底层线性代数运算默认使用自身的手写实现(部分优化了 NEON 指令),但也支持通过 接口适配 调用外部优化的 BLAS(基础线性代数子程序)和 LAPACK(线性代数包)库(如 APL、OpenBLAS、MKL 等)。
当 Eigen 检测到外部 BLAS/LAPACK 时,会优先调用这些库的高效实现(尤其是大矩阵运算,如 gemm 矩阵乘法、gesvd 奇异值分解等),从而提升性能。

二、具体步骤:用 APL 替代 Eigen 后端

1. 安装 Arm Performance Libraries

首先需要在目标 Arm 平台(如 A55 架构的边缘设备)上安装 APL。

  • 下载:从 Arm 官网获取适用于 Arm 64 位(aarch64)的 APL 安装包(需注册,Arm 开发者官网)。
  • 安装:按官方指南安装,默认路径通常为 /opt/arm/armpl/<版本号>/,包含头文件(include/)和库文件(lib/,如 libarmpl_lp64.so 等,lp64 对应 64 位模式)。

2. 配置 Eigen 以启用外部 BLAS/LAPACK 支持

Eigen 通过宏定义控制是否启用外部 BLAS/LAPACK,需在编译时指定相关宏和链接选项。

(1)修改 Eigen 配置(或编译宏)

在代码中 包含 Eigen 头文件前 定义以下宏,或在编译命令中通过 -D 传入:

cpp

// 启用 Eigen 对外部 BLAS 的支持
#define EIGEN_USE_BLAS
// 启用 Eigen 对外部 LAPACK 的支持(如需矩阵分解等高级功能)
#define EIGEN_USE_LAPACK

这些宏会让 Eigen 在编译时优先调用外部 BLAS(如 APL 的 cblas_* 函数)和 LAPACK(如 lapack_* 函数)。

(2)编译时链接 APL 库

编译代码时,需要指定 APL 的头文件路径、库路径,并链接 APL 的 BLAS/LAPACK 库。
以 g++ 为例,编译命令需包含:

bash

# 头文件路径(APL 的 include 目录)
-I/opt/arm/armpl/<版本号>/include
# 库文件路径(APL 的 lib 目录)
-L/opt/arm/armpl/<版本号>/lib
# 链接 APL 的 BLAS/LAPACK 库(根据 APL 版本可能需要调整库名)
-larmpl_lp64 -larmpl_lp64_mp # 64位模式的库,具体名称以安装目录为准
# 若 APL 依赖其他库(如 pthread),需补充链接
-lpthread -lm

3. 验证是否成功调用 APL

替换后需验证 Eigen 是否确实使用了 APL 的后端,避免仍使用 Eigen 自带实现:

  • 方法 1:编译日志检查
    编译时若出现类似 Eigen::blas::gemm 调用的提示(或无报错),说明宏定义生效。

  • 方法 2:性能对比
    运行包含大矩阵运算(如 matrixA * matrixBmatrix.jacobiSvd())的代码,对比替换前后的耗时。若 APL 适配正确,大矩阵运算性能应显著提升(小矩阵可能因调用开销差异不明显)。

  • 方法 3:工具调试
    使用 ldd 命令检查生成的可执行文件是否链接了 APL 的库(如 libarmpl_lp64.so):

    bash

    ldd your_executable | grep armpl

三、注意事项

  1. 适用场景
    APL 对 大矩阵运算(如维度 > 100 的矩阵乘法、SVD 分解)优化明显,而对小矩阵(如 VIO 中的 3x3 矩阵)可能不如 Eigen 自带的 NEON 优化高效(因函数调用开销占比高)。需结合实际算法(如 VINS-Fusion 中的滑窗优化、Fast-LIO 中的状态估计)的矩阵规模判断收益。

  2. 数据类型兼容性
    Eigen 默认使用列主序(Column-major)存储,与 BLAS 标准一致,无需调整数据布局;但需确保 Eigen 的数据类型(如 float/double)与 APL 编译时的精度(lp64 对应 64 位整数 + 32 位 float/64 位 double)匹配。

  3. 部分函数不支持
    Eigen 中的部分高级功能(如稀疏矩阵、自定义算子)可能不调用外部 BLAS/LAPACK,仍需依赖 Eigen 自身实现,需针对性优化(如手动调用 APL 接口)。

  4. 与其他优化结合
    替换后端后,可配合其他优化(如编译器 -O3 -march=armv8.2-a+neon 选项、OpenMP 多线程并行)进一步提升性能(APL 本身也支持多线程,可通过环境变量 ARMPL_NUM_THREADS 控制线程数)。

  5. 版本兼容性
    确保 Eigen 版本(建议 3.3 以上)支持外部 BLAS/LAPACK,且 APL 版本与目标 Arm 架构(如 A55 属于 Armv8.2-A)匹配,避免指令集不兼容(如 APL 编译时启用了 A55 不支持的指令)。

四、替代方案:直接调用 APL 接口

若 Eigen 对 APL 的适配不够理想,可在关键计算模块(如 VIO 中的协方差矩阵更新、LIO 中的点云配准)直接调用 APL 的 C 接口(如 cblas_sgemm 进行单精度矩阵乘法),跳过 Eigen 中间层,示例:

cpp

#include "armpl.h"  // APL 的头文件
// 计算 C = alpha*A*B + beta*C(A: MxK, B: KxN, C: MxN,列主序)
cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans,
M, N, K, alpha, A, lda, B, ldb, beta, C, ldc);

通过以上方法,可让 Arm 架构的边缘设备(如 8 核 A55)在路径规划、VIO、LIO 等算法的线性代数密集型任务中,充分利用 APL 的硬件优化,提升运行效率。